#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include "comunication.h"

#define h_addr h_addr_list[0]

void bcopy(const void *s1, void *s2, size_t n);
void bzero(void *s, size_t n);
int getdtablesize(void);

int udpsocket;

int tcpsocket;

struct sockaddr_in clt;

int get_udp_sock(){return udpsocket;}

int get_tcp_sock(){return tcpsocket;}

int bks_init_udp(){
	if((udpsocket = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
		perror("opening socket");
		return ERRORSOCKET;
	}
	return 0;
}


int bks_create_tcp(){
	int tcp;
	if((tcp = socket(PF_INET, SOCK_STREAM, 0)) < 0){
		perror("opening socket");
		return ERRORSOCKET;
	}
	return tcp;
}


int bks_init_server_socket(int port){
	struct sockaddr_in	udp_addr, tcp_addr;
	if((tcpsocket = socket(PF_INET, SOCK_STREAM, 0)) < 0){
		perror("opening socket");
		return ERRORSOCKET;
	}
/*	printf("server tcpsocket: %d\n", tcpsocket);
*/
	if((udpsocket = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
		perror("opening socket");
		return ERRORSOCKET;
	}
	/*printf("server udpsocket: %d\n", udpsocket);
*/
	bzero((char *) &udp_addr, sizeof(udp_addr));
	udp_addr.sin_family = AF_INET;	
	udp_addr.sin_addr.s_addr = INADDR_ANY;
	udp_addr.sin_port = htons(port);

	if(bind(udpsocket, (struct sockaddr *) &udp_addr, sizeof(udp_addr)) < 0){
      		perror("binding socket");
      		exit(-1);
	}
	bzero((char *) &tcp_addr, sizeof(tcp_addr));
	tcp_addr.sin_addr.s_addr = INADDR_ANY;
	tcp_addr.sin_family = AF_INET;
	tcp_addr.sin_port = htons(port);

	if((bind(tcpsocket, (struct sockaddr *)&tcp_addr, sizeof(tcp_addr))) < 0){
      		perror("binding socket");
      		exit(-1);
	}
	if(listen(tcpsocket, 5) <0){
		perror("listening tcpsocket");
		      		exit(-1);
	}

	return 0;
}


int bks_send_tcp(int sock, char pdu[]){

	int size = pdu[0];
	int _write;

	if ((_write = send(sock, pdu, size, 0)) < 0){
		return ERRORSENDTCP;
	}
	return 0;
}


int bks_rcv_tcp(int sock, char pdu[]){

	int pdusize, _read, to_read;
  int offset = 0;

	if ((pdusize = recv(sock, pdu, sizeof(char), MSG_PEEK)) <= 0){
		return ERRORRCVTCP;
	}

	pdusize = pdu[0];

	to_read = pdusize;

	while(to_read > 0){

		if ((_read = read(sock, pdu + offset, to_read)) <= 0){
			return ERRORRCVTCP;
		}
    offset += _read;

		to_read -= _read;
	}
	return 0;
}


int bks_send_client_udp(char pdu[], char* name, int port){

	struct sockaddr_in	_send;
	struct hostent		*hp;
	int	size;

	if ((hp = gethostbyname(name)) == 0){
		fprintf(stderr, "%s: unknown host\n", name);
		return ERRORHOST;
	}

	size = pdu[0];

	bcopy(hp->h_addr, &_send.sin_addr, hp->h_length);
	_send.sin_family = AF_INET;
	_send.sin_port = htons(port);
	if (sendto(udpsocket, pdu, size, 0, (struct sockaddr *)&_send, sizeof(_send)) < 0)
		return ERRORSENDUDP;

	return 0;
}


int bks_send_server_udp(char pdu[]){

	int	size = strlen(pdu) + 1;
	
	if (sendto(udpsocket, pdu, size, 0, (struct sockaddr *)&clt, sizeof(clt)) < 0)
		return ERRORSENDUDP;

	return 0;
}


int bks_rcv_udp(char pdu[]){

	int	sndsize, size;

	sndsize = sizeof(clt);
	if (recvfrom(udpsocket, pdu, sizeof(char), MSG_PEEK, (struct sockaddr *)&clt, (socklen_t *)&sndsize) < 0)
		return ERRORRCVUDP;
	size = pdu[0];
	if (recvfrom(udpsocket, pdu, size, 0, (struct sockaddr *)&clt, (socklen_t *)&sndsize) < 0)
		return ERRORRCVUDP;
	return 0;
}


int bks_accept(){

	struct sockaddr_in	_connecting;
	int	msgsock;

	int csize = sizeof(_connecting);

	if((msgsock = accept(tcpsocket, (struct sockaddr *) &_connecting, (socklen_t *)&csize)) < 0){
		return ERRORACCEPT;
	}
	
	return msgsock;
}


int bks_connect(int sock, char* name, int port){
	struct sockaddr_in	_connect;
	struct hostent		*hp;
	if ((hp = gethostbyname(name)) == 0){
		fprintf(stderr, "%s: unknown host\n", name);
		return ERRORHOST;
	}
	bcopy(hp->h_addr, &_connect.sin_addr, hp->h_length);
	_connect.sin_family = AF_INET;
	_connect.sin_port = htons(port);

	if (connect(sock, (struct sockaddr *) &_connect, sizeof(_connect)) < 0) {
		return ERRORCONNECT;
	}
	return 0;
}


void bks_disconnect(int sock){
	shutdown(sock, 2);
}


void bks_close(){
	close(tcpsocket);
	close(udpsocket);
}

int get_data_from(int sock1, int sock2){
	fd_set rmask;
	int n = 0;
	/* máscara de leitura */ /* Descritores de sockets */
	FD_ZERO(&rmask);
	FD_SET(sock1, &rmask);
	FD_SET(sock2, &rmask);
	n= select (getdtablesize(), &rmask, NO_DESC, NO_DESC, BLOCK);
	if (n <= 0)
		return 0;
	else {
		if (FD_ISSET(sock1, &rmask))
				return 1;
		if (FD_ISSET(sock2, &rmask))
			return 2;
	}
	return 0;
}



